/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 move_missile.cpp

	$Header: /heroes4/move_missile.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "move_missile.h"

#include "battlefield.h"
#include "bound_handler.h"
#include "combat_actor_action.h"
#include "combat_creature.h"
#include "game_time.h"
#include "options.h"

// -----------------------------------------------------------------
// class to move a missile across the battlefield
// -----------------------------------------------------------------
int const k_gravity = 2;

t_move_missile::t_move_missile( t_combat_actor& missile, 
							    t_combat_creature& attacker, 
								t_map_point_3d const& target_position, bool parabolic,
								t_missile_handler		end_handler )
 			  :  m_missile( missile ), m_attacker( &attacker ),
				 t_counted_animation( attacker.get_battlefield() ),
			    t_counted_idle_processor( 5000 / get_combat_animation_speed() )
{
	t_map_point_3d position = missile.get_position();
	t_direction    direction;

	m_is_parabolic = parabolic;
	m_target_position = target_position;
	m_delta = target_position - position;
	m_distance = sqrt( (double)(m_delta.row * m_delta.row + m_delta.column * m_delta.column) );
	m_distance_left = m_distance;
	m_last_update_time = get_time();
	m_delay = 1000 / missile.get_frames_per_second();
	m_move_distance = missile.get_walk_distance();
	m_sum.row = 0;
	m_sum.column = 0;
	m_sum.height = 0;
	m_end_handler = end_handler;
	direction = get_direction( position, target_position );
	m_battlefield.set_current_direction_and_action( missile, direction, k_combat_actor_action_walk );
	if (!parabolic)
		m_vertical_velocity = 0;
	else
	{
		int frames = (m_distance_left + m_move_distance - 1) / m_move_distance;

		m_vertical_velocity = frames * k_gravity;
	}
}

// -----------------------------------------------------------------
// class to move a missile across the battlefield
// -----------------------------------------------------------------

void t_move_missile::on_idle()
{
	t_uint32 new_time = get_time();

	// update animation (if any)
	if (elapsed_time( new_time, m_last_update_time ) >= m_delay )
	{
		int frame = m_missile.get_current_frame_num();

		frame++;
		if (frame >= m_missile.get_frame_count())
			frame = 0;
		m_battlefield.set_current_frame_num( m_missile, frame );
		m_last_update_time = new_time;
	}

	t_map_point_3d position;
	t_uint32       next_time = get_next_time() - get_delay();
	int			   skip_limit = 1;

	// move missile forward one space
	while (elapsed_time( new_time, next_time ) >= 0 && skip_limit-- > 0 )
	{
		next_time += get_delay();
		if (m_distance_left <= 0)
		{
			t_idle_ptr reference = this;

			suspend_idle_processing();
			m_battlefield.remove_object( &m_missile );
			animation_ended();
			m_end_handler( m_attacker, m_target_position );
			return;
		}

		t_map_point_3d delta;
		int            distance;

		// limit distance moved to distance left
		position = m_missile.get_position();
		distance = m_move_distance;
		if (distance > m_distance_left)
			distance = m_distance_left;
		m_distance_left -= distance;
		// add distance to accumulator
		m_sum += m_delta * distance;
		// find integer portion of moved distance
		delta = m_sum / m_distance;
		position += delta;
		m_sum -= delta * m_distance;
		// calculate parabolic arc
		if (m_is_parabolic)
		{
			position.height += m_vertical_velocity - k_gravity;
			m_vertical_velocity -= k_gravity << 1;
		}
	}
	set_next_time( next_time );
	m_battlefield.move_object( m_missile, position );
}
